﻿/******************************************************************************
 * * cale_main.c - implementation of calendar main functions
 *
 * *(C) Copyright 2019 Asr International Ltd.
 * * All Rights Reserved
 * ******************************************************************************/
#include "cale_global.h"

UI_Calendar_Mng_t g_CalendarMng;

/**
 * get week day of given day
 * param (in) DayInfo: UI_Calendar_Day_Info
 * return  UINT8: weekday
 */
UINT8 UI_Cale_Get_Weekday(lv_calendar_date_t *DayInfo)
{
    hal_rtc_t TmpTime;

    // get second of year begin
    TmpTime.tm_year = DayInfo->year;
    TmpTime.tm_mon  = DayInfo->month;
    TmpTime.tm_mday = DayInfo->day;
    TmpTime.tm_hour = 0;
    TmpTime.tm_min  = 0;
    TmpTime.tm_sec  = 0;

    return(UI_Rtc_Calc_Weekday(&TmpTime));
}

/**
 * get weeks from year begin of given day
 * param (in) void
 * return  UINT8: weeks from year begin
 */
UINT8 UI_Cale_Get_Weeks(lv_calendar_date_t *DayInfo)
{
    hal_rtc_t CurrentTime;
    INT16     TmpDayOffset;
    UINT8     CurrentWday;
    UINT32    YearBeginSnd;
    UINT32    CurrentSnd;
    UINT16    CurrentDays;

    // get second of year begin
    CurrentTime.tm_year = DayInfo->year;
    CurrentTime.tm_mon  = 1;
    CurrentTime.tm_mday = 1;
    CurrentTime.tm_hour = 0;
    CurrentTime.tm_min  = 0;
    CurrentTime.tm_sec  = 0;
    YearBeginSnd        = UI_Time_to_Seconds(&CurrentTime);

    // get days from year begin
    CurrentTime.tm_mon  = DayInfo->month;
    CurrentTime.tm_mday = DayInfo->day;
    CurrentSnd          = UI_Time_to_Seconds(&CurrentTime);
    CurrentDays         = (CurrentSnd - YearBeginSnd) / (24 * 3600);

    // get current weekday
    CurrentWday = UI_Rtc_Calc_Weekday(&CurrentTime);

    // get weeks from year begin
    TmpDayOffset = CurrentDays - CurrentWday;
    if (TmpDayOffset > 0)
    {
        return(TmpDayOffset / 7 + 2);
    }
    else
    {
        return(1);
    }
}

UINT16 const MonthAdd[12]            = { 0,31,59,90,120,151,181,212,243,273,304,334 };
UINT32 const LunarCalendarTable[199] =
{
    0x096D46,                                                                                  /*1970*/
    0x54AEBB,0x04AD4F,0x0A4D43,0x4D26B7,0x0D254B,0x8D52BF,0x0B5452,0x0B6A47,0x696D3C,0x095B50, /*1971-1980*/
    0x049B45,0x4A4BB9,0x0A4B4D,0xAB25C2,0x06A554,0x06D449,0x6ADA3D,0x0AB651,0x093746,0x5497BB, /*1981-1990*/
    0x04974F,0x064B44,0x36A537,0x0EA54A,0x86B2BF,0x05AC53,0x0AB647,0x5936BC,0x092E50,0x0C9645, /*1991-2000*/
    0x4D4AB8,0x0D4A4C,0x0DA541,0x25AAB6,0x056A49,0x7AADBD,0x025D52,0x092D47,0x5C95BA,0x0A954E, /*2001-2010*/
    0x0B4A43,0x4B5537,0x0AD54A,0x955ABF,0x04BA53,0x0A5B48,0x652BBC,0x052B50,0x0A9345,0x474AB9, /*2011-2020*/
    0x06AA4C,0x0AD541,0x24DAB6,0x04B64A,0x69573D,0x0A4E51,0x0D2646,0x5E933A,0x0D534D,0x05AA43, /*2021-2030*/
    0x36B537,0x096D4B,0xB4AEBF,0x04AD53,0x0A4D48,0x6D25BC,0x0D254F,0x0D5244,0x5DAA38,0x0B5A4C, /*2031-2040*/
    0x056D41,0x24ADB6,0x049B4A,0x7A4BBE,0x0A4B51,0x0AA546,0x5B52BA,0x06D24E,0x0ADA42,0x355B37, /*2041-2050*/
    0x09374B,0x8497C1,0x049753,0x064B48,0x66A53C,0x0EA54F,0x06B244,0x4AB638,0x0AAE4C,0x092E42, /*2051-2060*/
    0x3C9735,0x0C9649,0x7D4ABD,0x0D4A51,0x0DA545,0x55AABA,0x056A4E,0x0A6D43,0x452EB7,0x052D4B, /*2061-2070*/
    0x8A95BF,0x0A9553,0x0B4A47,0x6B553B,0x0AD54F,0x055A45,0x4A5D38,0x0A5B4C,0x052B42,0x3A93B6, /*2071-2080*/
    0x069349,0x7729BD,0x06AA51,0x0AD546,0x54DABA,0x04B64E,0x0A5743,0x452738,0x0D264A,0x8E933E, /*2081-2090*/
    0x0D5252,0x0DAA47,0x66B53B,0x056D4F,0x04AE45,0x4A4EB9,0x0A4D4C,0x0D1541,0x2D92B5           /*2091-2099*/
};

/**
 * get Lunar date from given day
 * param (in) year: INT32
 * param (in) month: INT32
 * param (in) day: INT32
 * param (out) LunarCalendarDay: UINT32 *
 * return  BOOL: true means leap month
 */
BOOL UI_Cale_Lunar_Calc(INT32 year,INT32 month,INT32 day, UINT32 *LunarYear, UINT32 *LunarCalendarDay)
{
    INT32 Spring_NY,Sun_NY,StaticDayCount;
    INT32 index,flag;

    if (((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0x0060) >> 5) == 1)
    {
        Spring_NY = (LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0x001F) - 1;
    }
    else
    {
        Spring_NY = (LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0x001F) - 1 + 31;
    }
    Sun_NY = MonthAdd[month - 1] + day - 1;
    if ((!(year % 4)) && (month > 2))
    {
        Sun_NY++;
    }

    if (Sun_NY >= Spring_NY)
    {
        *LunarYear = year;
        Sun_NY    -= Spring_NY;
        month      = 1;
        index      = 1;
        flag       = 0;
        if ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & (0x80000 >> (index - 1))) == 0)
        {
            StaticDayCount = 29;
        }
        else
        {
            StaticDayCount = 30;
        }
        while (Sun_NY >= StaticDayCount)
        {
            Sun_NY -= StaticDayCount;
            index++;
            if (month == ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0xF00000) >> 20))
            {
                flag = ~flag;
                if (flag == 0)
                {
                    month++;
                }
            }
            else
            {
                month++;
            }
            if ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & (0x80000 >> (index - 1))) == 0)
            {
                StaticDayCount = 29;
            }
            else
            {
                StaticDayCount = 30;
            }
        }
        day = Sun_NY + 1;
    }
    else
    {
        *LunarYear = year - 1;
        Spring_NY -= Sun_NY;
        year--;
        month = 12;
        if (((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0xF00000) >> 20) == 0)
        {
            index = 12;
        }
        else
        {
            index = 13;
        }
        flag = 0;
        if ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & (0x80000 >> (index - 1))) == 0)
        {
            StaticDayCount = 29;
        }
        else
        {
            StaticDayCount = 30;
        }
        while (Spring_NY > StaticDayCount)
        {
            Spring_NY -= StaticDayCount;
            index--;
            if (flag == 0)
            {
                month--;
            }
            if (month == ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0xF00000) >> 20))
            {
                flag = ~flag;
            }
            if ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & (0x80000 >> (index - 1))) == 0)
            {
                StaticDayCount = 29;
            }
            else
            {
                StaticDayCount = 30;
            }
        }
        day = StaticDayCount - Spring_NY + 1;
    }
    *LunarCalendarDay |= day;
    *LunarCalendarDay |= (month << 6);
    if (month == ((LunarCalendarTable[year - UI_CALE_MIN_YEAR] & 0xF00000) >> 20))
    {
        return(1);
    }
    else
    {
        return(0);
    }
} /* UI_Cale_Lunar_Calc */

/**
 * get Lunar from given day
 * param (in) *Date: lv_calendar_date_t
 * param (in) LunarDateStr: INT8*  lunar date string
 * return  VOID
 */
VOID UI_Cale_Get_Lunar(lv_calendar_date_t *Date, INT8 *LunarDateStr, INT8 *LunarYearStr)
{
    const INT8 *ChDay[] =
    {
        "*",  "初一",  "初二",  "初三",  "初四",  "初五",
        "初六", "初七",  "初八",  "初九",  "初十",
        "十一", "十二",  "十三",  "十四",  "十五",
        "十六", "十七",  "十八",  "十九",  "二十",
        "廿一", "廿二",  "廿三",  "廿四",  "廿五",
        "廿六", "廿七",  "廿八",  "廿九",  "三十"
    };
    const INT8 *szText1[10]     = { "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
    const INT8 *szText2[12]     = { "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };
    const INT8 *szText3[12]     = { "鼠", "牛", "虎", "免", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
    const INT8 *ChMonth[]       = { "*","正","二","三","四","五","六","七","八","九","十","十一","腊" };
    UINT32     LunarCalendarDay = 0;
    UINT32     LunarYear;

    if (UI_Cale_Lunar_Calc(Date->year, Date->month, Date->day, &LunarYear, &LunarCalendarDay))
    {
        strcat(LunarDateStr,"闰");
        strcat(LunarDateStr,ChMonth[(LunarCalendarDay & 0x3C0) >> 6]);
    }
    else
    {
        strcat(LunarDateStr,ChMonth[(LunarCalendarDay & 0x3C0) >> 6]);
    }

    strcat(LunarDateStr,"月");
    strcat(LunarDateStr,ChDay[LunarCalendarDay & 0x3F]);

    if (NULL != LunarYearStr)
    {
        sprintf(LunarYearStr, "%s%s %s年", szText1[(LunarYear - 4) % 10],szText2[(LunarYear - 4) % 12],
                szText3[(LunarYear - 4) % 12]);
    }
}

/**
 *
 * Create year roller options from selected year value
 * param (in) Year:         UINT16
 *       (out)YearOptions:  INT8*
 *       (in/out)Len:       UINT8
 * return void:
 */
VOID UI_Cale_Get_YearOptions_From_Year(UINT16 Year, INT8 *YearOptions, UINT8 *Len)
{
    UINT16 YearDisplayedInRoller[7];
    UINT8  i, pos = 0;

    memset(YearOptions, 0, *Len);
    // Create year roller options
    YearDisplayedInRoller[0] = ((Year - UI_CALE_MIN_YEAR + UI_CALE_YEAR_LOOP_CNT) - 3) % UI_CALE_YEAR_LOOP_CNT +
                               UI_CALE_MIN_YEAR;
    YearDisplayedInRoller[1] = ((Year - UI_CALE_MIN_YEAR + UI_CALE_YEAR_LOOP_CNT) - 2) % UI_CALE_YEAR_LOOP_CNT +
                               UI_CALE_MIN_YEAR;
    YearDisplayedInRoller[2] = ((Year - UI_CALE_MIN_YEAR + UI_CALE_YEAR_LOOP_CNT) - 1) % UI_CALE_YEAR_LOOP_CNT +
                               UI_CALE_MIN_YEAR;
    YearDisplayedInRoller[3] = Year;
    YearDisplayedInRoller[4] = (Year + 1 - UI_CALE_MIN_YEAR) % UI_CALE_YEAR_LOOP_CNT + UI_CALE_MIN_YEAR;
    YearDisplayedInRoller[5] = (Year + 2 - UI_CALE_MIN_YEAR) % UI_CALE_YEAR_LOOP_CNT + UI_CALE_MIN_YEAR;
    YearDisplayedInRoller[6] = (Year + 3 - UI_CALE_MIN_YEAR) % UI_CALE_YEAR_LOOP_CNT + UI_CALE_MIN_YEAR;
    // e.g. "2016\n2017\n2018\n2019\n2020\n2021\n2022"
    for (i = 0; i < 7; i++)
    {
        YearOptions[pos++] = YearDisplayedInRoller[i] / 1000 + 0x30;
        YearOptions[pos++] = (YearDisplayedInRoller[i] % 1000) / 100 + 0x30;
        YearOptions[pos++] = (YearDisplayedInRoller[i] % 100) / 10 + 0x30;
        YearOptions[pos++] = YearDisplayedInRoller[i] % 10 + 0x30;
        YearOptions[pos++] = 0x0A;
    }
    YearOptions[pos - 1] = 0;
    *Len                 = pos - 1;
}

/**
 *
 * Get year value from Year string of roller option
 * param (in) pYearOption: INT8 *
 * return UINT16: year
 */
UINT16 UI_Cale_Get_Year_From_YearOption(INT8 *pYearOption)
{
    UINT16 Year;

    Year = (pYearOption[0] - 0x30) * 1000 + (pYearOption[1] - 0x30) * 100 + (pYearOption[2] - 0x30) * 10 +
           (pYearOption[3] - 0x30);

    return(Year);
}

/**
 * Save Reminder to global variable and Nv for add reminder during view all or view today process
 * param (in) Hour: UINT8
 * param (in) Min: UINT8
 * return void
 */
VOID UI_Cale_Save_For_Add_Reminder(UINT8 Hour, UINT8 Min)
{
    NV_UI_Calendar_Reminder_t NvReminderInfo;
    UI_Reminder_t             *Reminder = NULL;
    UI_Reminder_t             *Node;
    UINT8                     Valid[NV_CALENDAR_MAX_REMINDER_NUM] = {0};
    UINT8                     i = 0;

    printf("%s: %d/%d/%d, %d:%d\n",
           __FUNCTION__,
           g_CalendarMng.DayOnShow.year,
           g_CalendarMng.DayOnShow.month,
           g_CalendarMng.DayOnShow.day,
           Hour, Min);
    g_CalendarMng.ReminderInOper->Info.Year = g_CalendarMng.DayOnShow.year;
    g_CalendarMng.ReminderInOper->Info.Mon  = g_CalendarMng.DayOnShow.month;
    g_CalendarMng.ReminderInOper->Info.Day  = g_CalendarMng.DayOnShow.day;
    g_CalendarMng.ReminderInOper->Info.Hour = Hour;
    g_CalendarMng.ReminderInOper->Info.Min  = Min;

    Node = _lv_ll_get_head(&g_CalendarMng.Reminder);
    while (Node)
    {
        if ((NULL == Reminder)
            && (0 > UI_Cale_Compare_Reminder_Time(&g_CalendarMng.ReminderInOper->Info, &Node->Info)))
        {
            Reminder = _lv_ll_ins_prev(&g_CalendarMng.Reminder, Node);
        }
        Valid[Node->IndexInNV] = 1;
        Node = _lv_ll_get_next(&g_CalendarMng.Reminder, Node);
    }
    if (NULL == Reminder)
    {
        Reminder = _lv_ll_ins_tail(&g_CalendarMng.Reminder);
    }
    Hal_Mem_Copy(&Reminder->Info, &g_CalendarMng.ReminderInOper->Info, sizeof(NV_UI_Calendar_Reminder_t));

    // write nvram
    Hal_Mem_Copy(&NvReminderInfo, &g_CalendarMng.ReminderInOper->Info, sizeof(NV_UI_Calendar_Reminder_t));
    for (i = 0;i < NV_CALENDAR_MAX_REMINDER_NUM;i++)
    {
        if (0 == Valid[i])
        {
            break;
        }
    }
    UI_NV_Write_Req(NV_SECTION_UI_CALENDAR,
                    i * sizeof(NV_UI_Calendar_Reminder_t),
                    sizeof(NV_UI_Calendar_Reminder_t), (UINT8 *)(&NvReminderInfo));
    Reminder->IndexInNV = i;

    Hal_Mem_Free(g_CalendarMng.ReminderInOper);
    g_CalendarMng.ReminderInOper = NULL;

    // inform alarm submodule
    if ((0xFF != Hour))
    {
        if (g_CalendarMng.AlarmReminder)
        {
            if (0 <= UI_Cale_Compare_Reminder_Time(&Reminder->Info,
                                                   &g_CalendarMng.AlarmReminder->Info))
            {
                /* the alarm time of the new reminder is equal to or later than
                 * the reminder which has been set to rtc */
                return;
            }
            else
            {
                g_CalendarMng.AlarmReminder = NULL;
            }
        }
        Alarm_Calendar_Alarm_Change_Ind(FALSE);
    }
}

/**
 * Save Reminder to global variable and Nv for edit reminder during view all or view today process
 * param (in) Hour: UINT8
 * param (in) Min: UINT8
 * return void
 */
VOID UI_Cale_Save_For_Edit_Reminder(UINT8 Hour, UINT8 Min)
{
    NV_UI_Calendar_Reminder_t NvReminderInfo;
    UI_Reminder_t             *Node;

    printf("%s: %d/%d/%d,old %d:%d,new %d:%d,%s\n", __FUNCTION__,
           g_CalendarMng.ReminderInOper->Info.Year, g_CalendarMng.ReminderInOper->Info.Mon,
           g_CalendarMng.ReminderInOper->Info.Day, g_CalendarMng.ReminderInOper->Info.Hour,
           g_CalendarMng.ReminderInOper->Info.Day, Hour, Min,
           g_CalendarMng.ReminderInOper->Info.Text);
    g_CalendarMng.ReminderInOper->Info.Hour = Hour;
    g_CalendarMng.ReminderInOper->Info.Min  = Min;
    snprintf(g_CalendarMng.ReminderInOper->Info.Text, NV_CALENDAR_MAX_REMINDER_LEN,
             "%s", g_CalendarMng.EditTxt);
    Hal_Mem_Free(g_CalendarMng.EditTxt);
    g_CalendarMng.EditTxt = NULL;

    Node = _lv_ll_get_head(&g_CalendarMng.Reminder);
    while (Node)
    {
        if ((g_CalendarMng.ReminderInOper != Node)
            && (0 > UI_Cale_Compare_Reminder_Time(&g_CalendarMng.ReminderInOper->Info, &Node->Info)))
        {
            break;
        }
        Node = _lv_ll_get_next(&g_CalendarMng.Reminder, Node);
    }
    _lv_ll_move_before(&g_CalendarMng.Reminder, g_CalendarMng.ReminderInOper, Node);

    // write nvram
    Hal_Mem_Copy(&NvReminderInfo, &g_CalendarMng.ReminderInOper->Info, sizeof(NV_UI_Calendar_Reminder_t));
    UI_NV_Write_Req(NV_SECTION_UI_CALENDAR,
                    g_CalendarMng.ReminderInOper->IndexInNV * sizeof(NV_UI_Calendar_Reminder_t),
                    sizeof(NV_UI_Calendar_Reminder_t), (UINT8 *)(&NvReminderInfo));

    // inform alarm submodule
    if (0xFF != Hour)
    {
        if (g_CalendarMng.AlarmReminder == g_CalendarMng.ReminderInOper)
        {
            g_CalendarMng.AlarmReminder = NULL;
        }
        else if (g_CalendarMng.AlarmReminder)
        {
            if (0 <= UI_Cale_Compare_Reminder_Time(&g_CalendarMng.ReminderInOper->Info,
                                                   &g_CalendarMng.AlarmReminder->Info))
            {
                return;
            }
            else
            {
                g_CalendarMng.AlarmReminder = NULL;
            }
        }
        Alarm_Calendar_Alarm_Change_Ind(FALSE);
    }
}

/**
 *
 * delete reminder from global variable and NV
 * param (in) void
 * return void
 */
VOID UI_Cale_Delete_Reminder(VOID)
{
    NV_UI_Calendar_Reminder_t NvReminderInfo = {};
    UI_Reminder_t             *Reminder;
    BOOL                      IsViewToday = FALSE;
    BOOL                      IsAlarm = FALSE;

    // inform Alarm module
    if (g_CalendarMng.AlarmReminder == g_CalendarMng.ReminderInOper)
    {
        g_CalendarMng.AlarmReminder = NULL;

        IsAlarm = TRUE;
    }

    // write nvram
    UI_NV_Write_Req(NV_SECTION_UI_CALENDAR,
                    g_CalendarMng.ReminderInOper->IndexInNV * sizeof(NV_UI_Calendar_Reminder_t),
                    sizeof(NV_UI_Calendar_Reminder_t), (UINT8 *)(&NvReminderInfo));

    if (UI_CALE_PROCESS_VIEW_TODAY == g_CalendarMng.Process)
    {
        IsViewToday = TRUE;
    }
    Reminder = UI_Cale_Get_Next_Reminder(g_CalendarMng.ReminderInOper, IsViewToday);
    printf("%s: %d/%d/%d,%d:%d,%s\n", __FUNCTION__,
           g_CalendarMng.ReminderInOper->Info.Year, g_CalendarMng.ReminderInOper->Info.Mon,
           g_CalendarMng.ReminderInOper->Info.Day, g_CalendarMng.ReminderInOper->Info.Hour,
           g_CalendarMng.ReminderInOper->Info.Min, g_CalendarMng.ReminderInOper->Info.Text);
    _lv_ll_remove(&g_CalendarMng.Reminder, g_CalendarMng.ReminderInOper);
    Hal_Mem_Free(g_CalendarMng.ReminderInOper);
    g_CalendarMng.ReminderInOper = Reminder;

    if (TRUE == IsAlarm)
    {
        Alarm_Calendar_Alarm_Change_Ind(FALSE);
    }
}

/**
 * get today Reminder Empty status
 * param (in) void
 * return BOOL: true means empty
 */
BOOL UI_Cale_Is_On_Show_Reminder_Empty(VOID)
{
    UI_Reminder_t *Reminder;

    Reminder = _lv_ll_get_head(&g_CalendarMng.Reminder);
    while (Reminder)
    {
        if ((Reminder->Info.Year == g_CalendarMng.DayOnShow.year)
            && (Reminder->Info.Mon == g_CalendarMng.DayOnShow.month)
            && (Reminder->Info.Day == g_CalendarMng.DayOnShow.day))
        {
            return(FALSE);
        }
        Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, Reminder);
    }

    return(TRUE);
}

/**
 * get next Reminder
 * param (in) CurReminder: UI_Reminder_t *
 * param (in) IsViewToday: BOOL
 * return UI_Reminder_t *
 */
UI_Reminder_t *UI_Cale_Get_Next_Reminder(UI_Reminder_t *CurReminder, BOOL IsViewToday)
{
    UI_Reminder_t *Reminder;
    BOOL          GetFirst = FALSE;

    if (NULL == CurReminder)
    {
        GetFirst = TRUE;
    }
    else
    {
        Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, CurReminder);
        if ((NULL == Reminder)
            || (Reminder && IsViewToday
                && ((Reminder->Info.Year != g_CalendarMng.DayOnShow.year)
                    || (Reminder->Info.Mon != g_CalendarMng.DayOnShow.month)
                    || (Reminder->Info.Day != g_CalendarMng.DayOnShow.day))))
        {
            GetFirst = TRUE;
        }
    }
    if (GetFirst)
    {
        Reminder = _lv_ll_get_head(&g_CalendarMng.Reminder);
        if (IsViewToday)
        {
            while (Reminder)
            {
                if ((Reminder->Info.Year == g_CalendarMng.DayOnShow.year)
                    && (Reminder->Info.Mon == g_CalendarMng.DayOnShow.month)
                    && (Reminder->Info.Day == g_CalendarMng.DayOnShow.day))
                {
                    break;
                }
                Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, Reminder);
            }
        }
    }

    return Reminder;
}

/**
 * get next Reminder index of today
 * param (in) IsViewToday: BOOL
 * return UI_Reminder_t *
 */
UI_Reminder_t *UI_Cale_Get_Prev_Reminder(UI_Reminder_t *CurReminder, BOOL IsViewToday)
{
    UI_Reminder_t *Reminder;

    if (NULL == CurReminder)
    {
        return NULL;
    }

    Reminder = _lv_ll_get_prev(&g_CalendarMng.Reminder, CurReminder);
    if ((NULL == Reminder)
        || (Reminder && IsViewToday
            && ((Reminder->Info.Year != g_CalendarMng.DayOnShow.year)
                || (Reminder->Info.Mon != g_CalendarMng.DayOnShow.month)
                || (Reminder->Info.Day != g_CalendarMng.DayOnShow.day))))
    {
        /*get the last reminder*/
        Reminder = _lv_ll_get_tail(&g_CalendarMng.Reminder);
        if (IsViewToday)
        {
            while (Reminder)
            {
                if ((Reminder->Info.Year == g_CalendarMng.DayOnShow.year)
                    && (Reminder->Info.Mon == g_CalendarMng.DayOnShow.month)
                    && (Reminder->Info.Day == g_CalendarMng.DayOnShow.day))
                {
                    break;
                }
                Reminder = _lv_ll_get_prev(&g_CalendarMng.Reminder, Reminder);
            }
        }
    }

    return Reminder;
}

/**
 * get reminder position of today
 * param (in) CurReminder: UI_Reminder_t *
 * param (in) IsViewToday: BOOL
 * return UINT8: index in global variable
 */
UINT8 UI_Cale_Get_Reminder_Position(UI_Reminder_t *CurReminder, BOOL IsViewToday)
{
    UINT8         Position = 0;
    UI_Reminder_t *Reminder;

    Reminder = _lv_ll_get_head(&g_CalendarMng.Reminder);
    while (Reminder)
    {
        if ((!IsViewToday)
            || (IsViewToday
                && (Reminder->Info.Year == g_CalendarMng.DayOnShow.year)
                && (Reminder->Info.Mon == g_CalendarMng.DayOnShow.month)
                && (Reminder->Info.Day == g_CalendarMng.DayOnShow.day)))
        {
            Position++;
        }
        if (CurReminder == Reminder)
        {
            break;
        }
        Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, Reminder);
    }

    return(Position);
}

/**
 * Timeout Call back function for ringing alarm
 * param (in) VOID
 * return  VOID
 */
VOID UI_Cale_Ring_Timeout_Cb(VOID *Para)
{
    If_Setting_Play_Tone_t *PlayToneReq;

    printf("%s\n", __FUNCTION__);

    g_CalendarMng.RingTimer = NULL;

    PlayToneReq = (If_Setting_Play_Tone_t *)Hal_Mem_Alloc(sizeof(If_Setting_Play_Tone_t));
    PlayToneReq->PlayCtrl = IF_SETTING_PLAY_TONS_STOP;
    PlayToneReq->ToneType = IF_SETTING_TONS_TYPE_CALENDAR;
    Setting_Play_Tone_Req(PlayToneReq);
}

/**
 * Call back function for alarm
 * param (in) VOID
 * return  VOID
 */
VOID UI_Cale_Alarm_Ring_Start(VOID)
{
    If_Setting_Play_Tone_t *PlayToneReq;
    ACTIVITY_ID            TopAct = Nav_Get_Top();
    hal_rtc_t              AlarmTime;

    printf("%s: top act id %d\n", __FUNCTION__, TopAct);
    if ((ACT_ID_BOOT_ANIMATION == TopAct)
        || (ACT_ID_WELCOME_NOTE == TopAct))
    {
        printf("%s: ring during power on\n", __FUNCTION__);
        return;
    }

    Hal_Rtc_Get_Alarm(RTC_ALARM_1, &AlarmTime);
    if (NULL == g_CalendarMng.AlarmReminder)
    {
        printf("%s: reminder has been deleted\n", __FUNCTION__);
    }
    else if ((AlarmTime.tm_min != g_CalendarMng.AlarmReminder->Info.Min)
        || (AlarmTime.tm_hour != g_CalendarMng.AlarmReminder->Info.Hour)
        || (AlarmTime.tm_mday != g_CalendarMng.AlarmReminder->Info.Day)
        || (AlarmTime.tm_mon != g_CalendarMng.AlarmReminder->Info.Mon)
        || (AlarmTime.tm_year != g_CalendarMng.AlarmReminder->Info.Year))
    {
        printf("%s: reminder has been deleted\n", __FUNCTION__);
    }
    else
    {
        printf("%s: %d/%d/%d, %d:%d\n",
               __FUNCTION__,
               g_CalendarMng.AlarmReminder->Info.Year,
               g_CalendarMng.AlarmReminder->Info.Mon,
               g_CalendarMng.AlarmReminder->Info.Day,
               g_CalendarMng.AlarmReminder->Info.Hour,
               g_CalendarMng.AlarmReminder->Info.Min);

        if ((ACT_ID_CALL_EMERGENCY_CALLING <= TopAct)
            && (ACT_ID_CALL_DTMF_INPUT >= TopAct))
        {
            // pending, buffer the calendar alarm event, handle when the call is ended
            printf("%s: during call\n", __FUNCTION__);
            return;
        }
        else if (ACT_ID_CALENDAR_REMINDER_ALARM_RING == TopAct)
        {
            // discard the reminder
            Nav_Back(ACT_ID_ANY);
        }
        else if ((ACT_ID_CALENDAR_ALARM_POSTPONED == TopAct)
            || (ACT_ID_CALENDAR_REMINDER_DISCARDED == TopAct))
        {
            if (g_CalendarMng.StatusTimer)
            {
                Hal_Timer_Stop(g_CalendarMng.StatusTimer);
                g_CalendarMng.StatusTimer = NULL;
            }
        }
        g_CalendarMng.RingReminder  = g_CalendarMng.AlarmReminder;
        g_CalendarMng.AlarmReminder = NULL;

        Display_Calendar_Reminder_Alarm_Ring();

        if (IF_FRAMEWORK_INIT_CHARGE == Framework_Get_Init_Type())
        {
            Framework_Display_Status_Bar(FALSE);
        }

        g_CalendarMng.RingTimer = NULL;
        if (FALSE == Alarm_Calendar_Check_Alarm_Ringing())
        {
            PlayToneReq = (If_Setting_Play_Tone_t *)Hal_Mem_Alloc(sizeof(If_Setting_Play_Tone_t));
            PlayToneReq->SimId    = MMI_MODEM_SIM_1;
            PlayToneReq->ToneType = IF_SETTING_TONS_TYPE_CALENDAR;
            PlayToneReq->PlayCtrl = IF_SETTING_PLAY_TONS_START;
            Setting_Play_Tone_Req(PlayToneReq);

            g_CalendarMng.RingTimer = Hal_Timer_Start(UI_CALENDAR_RING_TIME_LEN,
                                                      UI_Cale_Ring_Timeout_Cb,
                                                      NULL,
                                                      FALSE);

            Alarm_Calendar_Alarm_Change_Ind(TRUE);
        }
    }
}

/**
 * show lanar info on lunar cale
 * param (in) pDesc: VOID *
 * return  VOID
 */
VOID UI_Cale_Show_Lunar_Info(VOID *pDesc)
{
    UI_Normal_NoTitle_Desc_t *pTmpDesc;
    INT8                     TmpChar[80] = {};
    INT8                     LunarYear[20];
    INT8                     LunarDate[20] = {};
    INT8                     *WeekStr[7]   = { "日", "一", "二", "三", "四", "五", "六" };

    pTmpDesc = (UI_Normal_NoTitle_Desc_t *)pDesc;

    // get lunar
    UI_Cale_Get_Lunar(g_CalendarMng.LunarDate, LunarDate, LunarYear);

    // set reminder number
    sprintf(TmpChar, "\n%.4d年%.2d月%.2d日\n星期%s\n\n\n%s\n%s",g_CalendarMng.LunarDate->year,
            g_CalendarMng.LunarDate->month,g_CalendarMng.LunarDate->day,
            WeekStr[UI_Cale_Get_Weekday(g_CalendarMng.LunarDate)],LunarYear, LunarDate);
    lv_label_set_text(pTmpDesc->ContenCont.ContLabel.Label, TmpChar);
    lv_obj_align(pTmpDesc->ContenCont.ContLabel.Label,
                 lv_obj_get_parent(pTmpDesc->ContenCont.ContLabel.Label),
                 LV_ALIGN_IN_TOP_MID, 0, 0);
}

/**
 * Get hightlighted date information
 * param (in) Cnt:  UINT8 *
 * return  lv_calendar_date_t *
 */
lv_calendar_date_t *UI_Cale_Get_Hightlighted_Dates(UINT8 *Cnt)
{
    lv_calendar_date_t *Date = NULL;
    UI_Reminder_t      *Reminder;
    UINT8              DateCnt = 0;
    UINT16             Year    = 0;
    UINT8              Mon     = 0;
    UINT8              Day     = 0;
    UINT8              i       = 0;

    Reminder = _lv_ll_get_head(&g_CalendarMng.Reminder);
    while (Reminder)
    {
        if ((Reminder->Info.Year != Year)
            || (Reminder->Info.Mon != Mon)
            || (Reminder->Info.Day != Day))
        {
            DateCnt++;
            Year = Reminder->Info.Year;
            Mon  = Reminder->Info.Mon;
            Day  = Reminder->Info.Day;
        }
        Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, Reminder);
    }
    if (0 < DateCnt)
    {
        Date = (lv_calendar_date_t *)Hal_Mem_Alloc(sizeof(lv_calendar_date_t) * DateCnt);
        Reminder = _lv_ll_get_head(&g_CalendarMng.Reminder);
        Year = 0;
        Mon  = 0;
        Day  = 0;
        while (Reminder)
        {
            if ((Reminder->Info.Year != Year)
                || (Reminder->Info.Mon != Mon)
                || (Reminder->Info.Day != Day))
            {
                Year = Reminder->Info.Year;
                Mon  = Reminder->Info.Mon;
                Day  = Reminder->Info.Day;

                Date[i].year  = Year;
                Date[i].month = Mon;
                Date[i].day   = Day;
                i++;
            }
            Reminder = _lv_ll_get_next(&g_CalendarMng.Reminder, Reminder);
        }
    }
    else
    {
        //highlighted_dates can`t be NULL for lv_calendar_set_highlighted_dates
        Date = (lv_calendar_date_t *)Hal_Mem_Alloc(sizeof(lv_calendar_date_t));
    }

    *Cnt = DateCnt;

    return(Date);
}

/**
 * Display the reminder
 * param (in) pTmpDesc: UI_Normal_Reminder_Desc_t *
 * param (in) Reminder: UI_Reminder_t *
 * param (in) IsViewToday: BOOL
 * return  VOID
 */
VOID UI_Cale_Display_Reminder(UI_Normal_Reminder_Desc_t *pTmpDesc, UI_Reminder_t *Reminder, BOOL IsViewToday)
{
    INT8  Text[21] = {};
    UINT8 ReminderPos;
    lv_obj_t * Par = lv_obj_get_parent(pTmpDesc->TitleLabelL.Label);

    ReminderPos = UI_Cale_Get_Reminder_Position(Reminder, IsViewToday);
    snprintf(Text, 21, "%d", ReminderPos);
    lv_label_set_text(pTmpDesc->TitleLabelL.Label, Text);
    lv_obj_align(pTmpDesc->TitleLabelL.Label, Par, LV_ALIGN_IN_LEFT_MID, 6, 0);

    if (UI_LANG_EN == lv_lang_act())
    {
        if (0xFF == Reminder->Info.Hour)
        {
            sprintf(Text, "%.2d.%.2d", Reminder->Info.Day, Reminder->Info.Mon);
        }
        else
        {
            sprintf(Text, "%s %.2d:%.2d %.2d.%.2d",
                    LV_SYMBOL_BELL,
                    Reminder->Info.Hour,
                    Reminder->Info.Min,
                    Reminder->Info.Day,
                    Reminder->Info.Mon);
        }
    }
    else
    {
        if (0xFF == Reminder->Info.Hour)
        {
            sprintf(Text, "%.2d.%.2d", Reminder->Info.Mon, Reminder->Info.Day);
        }
        else
        {
            sprintf(Text, "%s %.2d:%.2d %.2d.%.2d",
                    LV_SYMBOL_BELL,
                    Reminder->Info.Hour,
                    Reminder->Info.Min,
                    Reminder->Info.Mon,
                    Reminder->Info.Day);
        }
    }
    lv_label_set_text(pTmpDesc->TitleLabelR.Label, Text);
    lv_obj_align(pTmpDesc->TitleLabelR.Label, Par, LV_ALIGN_IN_RIGHT_MID, -6, 0);

    // set reminder txt
    lv_label_set_text(pTmpDesc->ContLabel.Label, Reminder->Info.Text);
}

/**
 * Compare the time between two reminders
 * param (in) New: NV_UI_Calendar_Reminder_t *
 * param (in) Old: NV_UI_Calendar_Reminder_t *
 * return  INT8: -1 if New is earlier than Old, 0 if the same time, 1 if New is later than Old
 */
INT8 UI_Cale_Compare_Reminder_Time(NV_UI_Calendar_Reminder_t * New, NV_UI_Calendar_Reminder_t * Old)
{
    if (New->Year < Old->Year)
    {
        return(-1);
    }
    else if (New->Year > Old->Year)
    {
        return(1);
    }

    if (New->Mon < Old->Mon)
    {
        return(-1);
    }
    else if (New->Mon > Old->Mon)
    {
        return(1);
    }

    if (New->Day < Old->Day)
    {
        return(-1);
    }
    else if (New->Day > Old->Day)
    {
        return(1);
    }

    if (0xFF == New->Hour)
    {
        return(-1);
    }
    else if (0xFF == Old->Hour)
    {
        return(1);
    }

    if (New->Hour < Old->Hour)
    {
        return(-1);
    }
    else if (New->Hour > Old->Hour)
    {
        return(1);
    }

    if (New->Min < Old->Min)
    {
        return(-1);
    }
    else if (New->Min > Old->Min)
    {
        return(1);
    }
    else
    {
        return(0);
    }
}
